後端完成後,就要來介紹前端如何搭配 TypeScript 來開發,這個章節我們會使用 React 來實作簡單的任務管理網站,並串接我們寫好的後端,而會選擇使用 React 的原因也是非常簡單。
本篇會先介紹如何初始化一個 React + TypeScript 專案。
目前 React 專案有很多建置方式,官網推薦使用成熟的框架來建立 React,例如:Next.js、Remix、Gatsby,但我們只是要實作小專案,所以我們採用 Vite
來建立前端專案模板,這樣做的好處是,我們前期不用花太多心力在設定模組打包工具。
首先我們先輸入指令來初始化專案,其中 frontend
可以改成你要的專案名稱
npm create vite@latest frontend -- --template react-ts
接著輸入指令來安裝依賴套件
npm install
npm i react-router-dom zod
npm i -D @types/node
而我們有另外安裝 react-router-dom
,是讓 React 可以更方便的實作路由的套件。
可以看到這個模板生成了三個 tsconfig 的設定檔(之前只有生成兩個檔案,未來不確定會不會有變動)
我們在 tsconfig.app.json
內多增加一些設定,讓我們之後串接 API 時,可以更方便的拿到後端定義好的型別。
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
"@models/*": ["../backend/src/models/*"],
},
}
}
因為我們是使用 Vite
來做我們的模組打包工具,所以除了 tsconfig 要設定別名路徑外,vite.config.ts
也要設定別名路徑,才能在專案中使用。
import { defineConfig } from 'vite';
import path from 'path';
import react from '@vitejs/plugin-react';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'@models': path.resolve(__dirname, '../backend/src/models'),
},
},
})
(後來想了一下,後端那個專案也可以設定別名路徑,可以更優雅的引入檔案)
當我們已經安裝好相關的依賴後,接下來只要輸入指令就可以直接啟動專案。
npm run dev
是不是很快就啟好這個範例專案了!同時還可以按中間的按鈕一直增加數字。
在 React 中,元件是構建 UI 的核心單位。每個元件可以是一個獨立的模組,負責處理頁面中的一個小部分。接下來我們來實作一個簡單的 React 元件範例。
首先我們先新增一個 components
資料夾,用來放所有元件,並新增一個 Counter.tsx
,可以注意到副檔名是 tsx
,它跟 js
與 jsx
的關係一樣,原本是為了讓編譯器可以知道這個檔案 TypeScript + HTML 標籤的程式碼,同時也可以讓人更快速的知道這個檔案是做什麼的。
關於 React Component Props 的定義,大多數都會習慣定義成 XXXProps
算是不成文但大家都同意的規定,但 type
與 interface
的選擇就有各自的想法,只要專案統一就可以了。
// Counter.tsx
type CounterProps = {
count: number;
onIncrement: () => void;
onDecrement: () => void;
};
目前 React 的版本,新的元件通常都會是用 function
的方式實作,而 function
的撰寫方式又很多種,我個人是習慣使用 function
的方式撰寫,因為如果元件要使用泛型時,這種撰寫方式會更加統一。
// Counter.tsx
// ...
export default function Counter({ count, onIncrement, onDecrement }: CounterProps) {
return (
<div>
<p>Count: {count}</p>
<button onClick={onIncrement}>Increment</button>
<button onClick={onDecrement}>Decrement</button>
</div>
);
}
這邊也展示其他元件的撰寫方式:
tsx
同時有 TypeScript + HTML,所以編譯器在遇到泛型時,會不知道是泛型還是 HTML,所以需要添加一個 ,
來告訴編譯器這個不是 HTML。
const Component = <T,>(props: ComponentProps<T>) => // ...
export default Component
Component Type
,但卻犧牲了泛型的彈性,所以被很多開發者吐槽不好使用。
import { FC } from 'react';
const Component: FC<ComponentProps> = (props) => // ...
export default Component
export default function Component<T>(props: ComponentProps<T>) => // ...
為了更方便的觀察我們實作的 Component,所以把一些多餘的程式碼移除。
import { useState } from 'react';
import Counter from './components/Counter';
import './App.css';
function App() {
const [count, setCount] = useState(0);
return (
<div className="card">
<Counter
count={count}
onIncrement={() => setCount((count) => count + 1)}
onDecrement={() => setCount((count) => count - 1)}
/>
</div>
);
}
export default App;
並可以看到畫面也跟著變成我們所寫的元件,並且能操作數字加減了!
這篇文章介紹了如何用 Vite 建立 React + TypeScript 專案,調整別名路徑設定,並讓它運作起來。並實作了一個最簡單的 Component。
接下來幾篇,我們可以開始實作前端,包含如何利用泛型來製作清單元件、表單元件串接 API、路由規劃與錯誤處理。
本篇程式碼變更可以看此 PR